package com.yym.project.utils;

import java.sql.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Properties;
//工具类
public class Dbutil {
    // 连接信息
    private static   String  username=null;
    private static   String  url=null;
    private static   String  password=null;
    private static   String  driver=null;
    //声明一个ThreadLocal 用于保存连接对象（同一个线程共享）
    private static ThreadLocal<Connection> localConn = new ThreadLocal<>();



    //使用连接信息（读取配置信息，什么时候去读呢？）
    static {
        try {
            //读取配置信息
            Properties pro = new Properties();// 准备map集合
            //把 db.properties 中的数据导入到 pro 集合中
            // 获得当前线程 Thread.currentThread()
            // 利用线程或获得类加载器(加载类) getContextClassLoader()
            // 类加载器就可以中类路径下加载资源 getResourceAsStream("db.properties")
            pro.load( Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties") );
            //赋值
            driver= pro.getProperty("jdbc.driver");
            username=pro.getProperty("jdbc.username");
            password=pro.getProperty("jdbc.password");
            url=pro.getProperty("jdbc.url");
            //加载驱动
            Class.forName(driver);

        }catch( Exception e){
            e.printStackTrace();
        }
    }




    //获得连接的方法
    public static Connection getConnection(){
        //从当前线程（执行这这段代码的线程 ）中获得连接对象
        if( localConn.get() ==null ){ // 当前线程中还没有连接对象
            try {
                //创建连接对象
                Connection conn = DriverManager.getConnection(url,username,password);
                //添加到当前线程中去
                localConn.set(conn);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //返回现有的
        Connection conn= localConn.get();
        return conn;
    }


    /**********************************
     * 释放资源方法
     */
    //关闭资源的方法
    public static void close(Connection conn){
         if(conn!=null){
             try {
                 conn.close();
                 localConn.remove();

             } catch (SQLException e) {
                 e.printStackTrace();
             }
         }
    }
    //关闭资源的方法
    public static void close(Connection conn, Statement smt){

        if(smt!=null){
            try {
                smt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try {
                conn.close();
                localConn.remove();

            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    //关闭资源的方法
    public static void close(Connection conn, Statement smt, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(smt!=null){
            try {
                smt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        if(conn!=null){
            try {
                conn.close();
                localConn.remove();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    /*************************************************
     * 日期转换方法
     */
    //日期转换 util.Date=> sql.Date
    public static Date toSQLDate( java.util.Date date ){
        Date sqlDate = new Date( date.getTime() );
        return sqlDate;
    }
    //日期转换 String => util.Date
    public static java.util.Date toUtilDate( String str  ) {
        //格式化工具
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        try {
            //把日期字符串转 Date
           return simpleDateFormat.parse(str);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return  null;
    }





    /*******************************
     * 事务控制方法
     */
    //开始事务
    public static void begin(){
        try {
            getConnection().setAutoCommit(false); //设置手动事务
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    //提交事务
    public static  void commit(){
        try {
            getConnection().commit(); //提交
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            Dbutil.close(getConnection());
            localConn.remove(); // 移除连接对象
        }
    }
    //回滚事务
    public static  void rollback(){
        try {
            getConnection().rollback(); //回滚
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            Dbutil.close(getConnection());
            localConn.remove(); //移除,和线程解除绑定
        }
    }


    //封装一个执行增删改的通用方法( 不同的SQL参数类型 个数不一致，使用可变参数 )
    /**
     * 执行DML语句的方法
     * @param connection 连接
     * @param sql   语句
     * @param args  参数
     * @return
     */
    public static int executeUpdate(Connection connection ,String sql ,  Object...args) throws Exception {
        //创建语句对象
        PreparedStatement psmt = connection.prepareStatement(sql);
        //替换？占位符 （不知道？号的个数根据 args反推）
        for(int i=0;i<args.length;i++){
             psmt.setObject(i+1,args[i]);
        }
        int row =psmt.executeUpdate();
        psmt.close();
        return row;
    }

    //查询封装,执行查询的功能
    // 1. 与DML不同（dml 返回int） 查询是的返回更加复杂（可能是对象 可能是集合，不同类型的对象）使用泛型
    // 2. 封装的基本思路是 相同的代码集中写在一起（不同的部分无法封装）。
    // 3  接口回调

    public static  <T> T query( Connection conn,String sql,RowMapper<T> rp,  Object...args) throws Exception{
        // 查询的核心是吧ResultSet中的数据。
        // 取出来然后 封装成 需要的对象 或 集合。
        // 不用的 对象属类型 不一样的 属性不一样 无法封装（排除反射）。
        PreparedStatement psmt = conn.prepareStatement(sql);
        //替换？占位符 （不知道？号的个数根据 args反推）
        for(int i=0;i<args.length;i++){
            psmt.setObject(i+1,args[i]);
        }
        ResultSet rs = psmt.executeQuery();
        //这里 ResultSet 转换成什么鬼？交给RowMapper
        T t = rp.mappperTo(rs);
        Dbutil.close(conn,psmt,rs);
        return t;
    }







}
